﻿using System;
using System.Collections.Generic;
using System.ComponentModel;
using System.ComponentModel.Design;
using System.Drawing.Design;
using System.IO;
using System.Linq;
using System.Windows.Forms;
using System.Windows.Forms.Design;
using Microsoft.WowAddonStudio.FrameXml.Serialization.Attributes;
using Microsoft.WowAddonStudio.FrameXml.Serialization.Enumerations;
using Microsoft.WowAddonStudio.FrameXml.Services;

namespace Microsoft.WowAddonStudio.FrameXml.Components.Editors
{
	/// <summary>
	/// 
	/// </summary>
	public class TreePickerEditor : UITypeEditor
	{
        protected static readonly Dictionary<FileType, PickerElement> treeMap = new Dictionary<FileType, PickerElement>();
        protected static PickerElement element;
        protected static readonly object treeLock = new object();

        /// <summary>
        /// 
        /// </summary>
        protected static readonly Dictionary<FileType, string> extensionRootMap = new Dictionary<FileType, string>
		                                                                      	{
		                                                                      		{FileType.Model, "Interface"},
		                                                                      		{FileType.Image, "Interface"},
		                                                                      		{FileType.Font, "Fonts"},
		                                                                      	};

        /// <summary>
        /// 
        /// </summary>
        protected static readonly Dictionary<FileType, Type> extensionEditorFormType = new Dictionary<FileType, Type>
		                                                                      	{
		                                                                      		{FileType.Image, typeof(TextureTreePickerEditorForm)},
		                                                                      		{FileType.Font, typeof(FontTreePickerEditorForm)},
		                                                                      		{FileType.Model, typeof(TreePickerEditorForm)}
		                                                                      	};

        /// <summary>
        /// 
        /// </summary>
        protected static readonly Dictionary<string, FileType> fileExtensionMap = new Dictionary<string, FileType>
                                                                         {
                                                                             {".blp", FileType.Image },
                                                                             {".png", FileType.Image },
                                                                             {".tga", FileType.Image },
                                                                             {".m2", FileType.Model },
                                                                             {".ttf", FileType.Font }
                                                                         };

		/// <summary>
		/// Gets the editor style used by the <see cref="M:System.Drawing.Design.UITypeEditor.EditValue(System.IServiceProvider,System.Object)"/> method.
		/// </summary>
		/// <param name="context">An <see cref="T:System.ComponentModel.ITypeDescriptorContext"/> that can be used to gain additional context information.</param>
		/// <returns>
		/// A <see cref="T:System.Drawing.Design.UITypeEditorEditStyle"/> value that indicates the style of editor used by the <see cref="M:System.Drawing.Design.UITypeEditor.EditValue(System.IServiceProvider,System.Object)"/> method. If the <see cref="T:System.Drawing.Design.UITypeEditor"/> does not support this method, then <see cref="M:System.Drawing.Design.UITypeEditor.GetEditStyle"/> will return <see cref="F:System.Drawing.Design.UITypeEditorEditStyle.None"/>.
		/// </returns>
		public override UITypeEditorEditStyle GetEditStyle(ITypeDescriptorContext context)
		{
			return UITypeEditorEditStyle.Modal;
		}

		/// <summary>
		/// Edits the specified object's value using the editor style indicated by the <see cref="M:System.Drawing.Design.UITypeEditor.GetEditStyle"/> method.
		/// </summary>
		/// <param name="context">An <see cref="T:System.ComponentModel.ITypeDescriptorContext"/> that can be used to gain additional context information.</param>
		/// <param name="provider">An <see cref="T:System.IServiceProvider"/> that this editor can use to obtain services.</param>
		/// <param name="value">The object to edit.</param>
		/// <returns>
		/// The new value of the object. If the value of the object has not changed, this should return the same object it was passed.
		/// </returns>
		public override object EditValue(ITypeDescriptorContext context, IServiceProvider provider, object value)
		{
			if (provider != null)
			{
				if (context.PropertyDescriptor.Attributes != null)
				{
					ReadOnlyAttribute attribute = context.PropertyDescriptor.Attributes.OfType<ReadOnlyAttribute>().FirstOrDefault();
					if (attribute != null)
					{
						if (attribute.IsReadOnly) return value;
					}
				}

				CreateResourceTree(context, provider);

				var editorService = (IWindowsFormsEditorService) provider.GetService(typeof (IWindowsFormsEditorService));

                FileType currentFileType = ExtractFileType(context);
                Type editorFormType = typeof(TreePickerEditorForm);

                if (extensionEditorFormType.ContainsKey(currentFileType))
                    editorFormType = extensionEditorFormType[currentFileType];

                using (var editorForm = (TreePickerEditorForm)Activator.CreateInstance(editorFormType, element, provider))
				{
					var text = (string) value;
					editorForm.RelativePath = text;
					if (editorService.ShowDialog(editorForm) == DialogResult.OK)
					{
						return editorForm.RelativePath;
					}
				}
			}

			return base.EditValue(context, provider, value);
		}

		/// <summary>
		/// Creates the tree.
		/// </summary>
		/// <param name="context">The context.</param>
		/// <param name="provider">The provider.</param>
        protected void CreateResourceTree(ITypeDescriptorContext context, IServiceProvider provider)
		{
			// Extract file type
            FileType currentFileType = ExtractFileType(context);
            string rootName = extensionRootMap[currentFileType];

            treeMap.TryGetValue(currentFileType, out element);
			ResourceFileLoader resourceFileLoader = GetResourceFileLoader(provider);

			lock (treeLock)
			{
				treeMap.Clear();
				element = new FolderPickerElement(rootName);

				foreach (var resourceFile in resourceFileLoader.GetResourceFiles())
				{
				    string extension = Path.GetExtension(resourceFile.Path).ToLower();
                    
                    if(fileExtensionMap.ContainsKey(extension))
                    {
                        if (fileExtensionMap[extension] == currentFileType)
                            element.Add(resourceFile);
                    }
				}

                treeMap.Add(currentFileType, element);
			}
		}

		/// <summary>
		/// Gets the resource file loader.
		/// </summary>
		/// <param name="provider">The provider.</param>
		/// <returns></returns>
        protected static ResourceFileLoader GetResourceFileLoader(IServiceProvider provider)
		{
			var resourceFileLoader = (ResourceFileLoader) provider.GetService(typeof (ResourceFileLoader));
			if (resourceFileLoader == null)
			{
				var designSurface = (DesignSurface) provider.GetService(typeof (DesignSurface));

				if (designSurface != null)
				{
					resourceFileLoader = (ResourceFileLoader) designSurface.GetService(typeof (ResourceFileLoader));
				}
			}
			return resourceFileLoader;
		}

		/// <summary>
		/// Extracts the file type.
		/// </summary>
		/// <param name="context">The context.</param>
		/// <returns></returns>
        protected virtual FileType ExtractFileType(ITypeDescriptorContext context)
		{
			var attribute = context.PropertyDescriptor.Attributes.OfType<FileAttribute>().FirstOrDefault();

		    if (attribute != null)
		        return attribute.FileType;

		    return FileType.Unknown;
		}
	}
}